#ifndef _SIGNAL_H_
#define _SIGNAL_H_

#include <functional>
#include <vector>
#include <SECore/Cloneable.h>
#include <memory>

namespace SEngine
{

    class ISignal
    {
    public:
        class LifeStamp {
        public:
            LifeStamp() : m_flag(std::make_shared<int>()) {}
            virtual ~LifeStamp() { m_flag.reset(); }
            std::weak_ptr<int> Watch() { return std::weak_ptr<int>(m_flag); }
        private:
            std::shared_ptr<int> m_flag;
        };
        virtual ~ISignal() = default;
        virtual std::unique_ptr<ISignal> Clone() = 0;

    };

    enum class SlotState
    {
        Keep = 0,
        Once
    };

    template<typename... Args>
    class Signal : public ISignal, public Cloneable<Signal<Args...>>
    {
    public:
        using Fn = std::function<SlotState(Args...)>;
    public:
        std::unique_ptr<ISignal> Clone() override {
            return this->CloneImpl();
        }

        void Invoke(Args... args) {
            m_slots.erase(std::remove_if(m_slots.begin(), m_slots.end(), [&](Fn & fn) {
                return fn(args...) == SlotState::Once;
            }), m_slots.end());
        }

        void AddSlot(Fn && fn) {
            m_slots.emplace_back(std::forward<Fn>(fn));
        }

        void AddSlot(Fn && fn, LifeStamp & stamp) {
            m_slots.emplace_back([stamp = stamp.Watch(), fn](Args... args) {
                if (stamp.expired()) return SlotState::Once;
                return fn(args...);
            });
        }

        void Clear() { m_slots.clear(); }

    private:
        std::vector<Fn> m_slots;
    };
} // namespace SEngine


#endif